/**
 * 20220304:[require] to [import]...jordia
 * 20221018: restructuring with typescript, major alteration.
 */
 import {Mesh, Object3D} from 'three';
import { Loose } from './utils';

 const NULL=new Object3D();
 const children:Loose<Array<number>>={};
 /**
  * Query, record and obtain objects from imported model files
  */
 export default class Child{
     constructor(){
         console.error('do not instantiate this class,use static methods provided!');
     }
 
     public static dispose():void{
         for(let key in children){
             delete children[key];
         }
     }
 
     /**
      * Record Target Object
      */
     public static set(key:string,location:Array<number>):void{
         children[key]=location;
     }
 
     /**
      * Get the recorded object (used when the object parent is not changed)
      */
     public static get(object:Object3D,nameKey:string):Object3D|null{
         const name=children[nameKey];
         if(!name){
             console.warn('no child record was found,check the '+nameKey+'!');
             return null;
         }
         if(!(object.children)){
             console.warn('cannot get child from '+object+'!');
             return null;
         }
         const cd=(index:number,obj:Object3D)=>{
             return obj.children[index];
         };
         let temp=object;
         for(let i of name){
             temp=cd(i,temp);
         }
         if(!temp)console.warn('no child was found!');
         return temp;
     }
 
     /**
      * Extract the recorded objects (used when the parent of the object needs to be changed, 
      *     and empty objects will be used as placeholders)
      */
     public static extract(object:Object3D,nameKey:string):Object3D|null{
         const name=children[nameKey];
         if(!name){
             console.warn('no child record was found,check the '+nameKey+'!');
             return null;
         }
         if(!(object.children)){
             console.warn('cannot get child from '+object+'!');
             return null;
         }
         const cd=(index:number,obj:Object3D,replace:boolean)=>{
             let tar=obj.children[index];
             if(replace)obj.children[index]=NULL;
             return tar;
         };
         let temp=object;
         for(let n=0;n<name.length;n++){
             let i=name[n];
             temp=cd(i,temp,n===name.length-1);
         }
         if(!temp)console.warn('no child was found!');
         return temp;
     }
 
     /**
      * Get the object named name in the object
      * type:Query type (i.e. the directory where the [name] key is located)
      *     none：object.name，
      *     material：object.material.name,
      *     userData：object.userData.name
      */
     public static search(name:string,object:Object3D,type:'none'|'material'|'userData'='none'):Array<number>{
         return Child._searchInternal(name,object,type);
     }
 
     /**
      * Internal query method
      */
     private static _searchInternal(strName:string,object:Object3D,type:string,indices:Array<number>=[],t:Array<number>=[]):Array<number>{
         for(let i=0;i<object.children.length;i++){
             const c=object.children[i];
             let sus=false;
             switch (type) {
                 case 'none':
                     if(c.name!==undefined)sus=c.name===strName;
                     break;
                 case 'material':
                    const material=(c as Mesh).material as any;
                     if(material!==undefined&&material.name!==undefined)sus=material.name===strName;
                     break;
                 case 'userData':
                     if(c.userData!==undefined&&c.userData.name!==undefined)sus=c.userData.name===strName;
                     break;
             }
             if(sus){
                 t.push(...indices,i);
                 return t;
             }else{
                 const ts=Child._searchInternal(strName,c,type,[...indices,i],t);
                 if(ts.length>0){
                     return ts;
                 }
             }
         }
         return t;
     }
 }